# 自定义序列化器类
# 文件名和自定义类名给根据需求随意定义
# 继承DRF的序列化器类
from rest_framework import serializers

from books.models import BookInfo


class HeroSerialzier(serializers.Serializer):
    """
        英雄序列化器
    """
    hname = serializers.CharField()
    hgender = serializers.IntegerField()
    hcomment = serializers.CharField()

    # 嵌套返回图书 定义字典表示图书数据 使用英雄表的关联外键字段
    # 一个英雄对应的是一一本图书 不需要many参数
    # hbook = serializers.PrimaryKeyRelatedField(read_only=True)
    hbook = serializers.StringRelatedField(read_only=True)


class BookSerialzier(serializers.Serializer):
    # 指定序列化返回的字段  根据模型类指定，也可根据业务需求指定。序列化器的字段类型和模型类的字段类型是一样的
    # id不需要参与反序列化的验证保存，只需要进行结果返回
    # read_only 参数规定了该字段只参与序列化返回过程
    id = serializers.IntegerField(read_only=True)
    # max_length 最大长度 min_length 最小长度 图书书名范围不能小于四个字，不能大于10字
    btitle = serializers.CharField(max_length=10, min_length=4)
    # write_only 参数规定了该字段只参与反序列化过程
    bpub_date = serializers.DateField(write_only=True)
    bread = serializers.IntegerField(max_value=100, min_value=0, default=0)
    bcomment = serializers.IntegerField(default=0)

    # 表示前端嵌套返回英雄信息的字段，图书是主表，英雄是副表
    # 返回数据有三种形式，
    # 第一种返回对应的id值 PrimaryKeyRelatedField会将关联的英雄id返回,一本图书对应多个英雄信息，所以在进行结果返回是需要加上many参数
    # heroinfo_set = serializers.PrimaryKeyRelatedField(read_only=True, many=True)
    # 第二种， StringRelatedField 返回关联表模型类中的__str__方法的数据内容,一本图书对应多个英雄信息，所以在进行结果返回是需要加上many参数
    # heroinfo_set =serializers.StringRelatedField(many=True)
    # 第三种，单独定义关联表序列化器，根据定义的序列化器字段返回
    # heroinfo_set = HeroSerialzier(many=True)

    # 单一字段验证方法 书写形式validate_字段名
    def validate_btitle(self, attrs):
        """
        :param attrs: 接受要验证字段的数据
        :return:
        """
        # 验证图书的名字是否是python，是 python则报错
        if attrs == "python":
            # 该异常会被视图中的errors接受
            raise serializers.ValidationError('书名不能为python')
        # 注意：验证没有问题将验证后的数据返回！！！！！
        return attrs

    # 多个字段验证
    def validate(self, attrs):
        """
        :param attrs: 接受的是所有验证验证的字段数据
        :return:
        """
        # 验证阅读量不能小于评论量
        if attrs['bread'] < attrs['bcomment']:
            raise serializers.ValidationError('读量不能小于评论量')

        return attrs

    # 封装保存业务逻辑方法
    def create(self, validated_data):
        """

        :param validated_data: 验证后的数据
        :return:
        """
        book = BookInfo.objects.create(**validated_data)

        # 保存完成后需要将保存后的数据对象返回
        return book

    # 封装更新业务逻辑方法
    def update(self, instance, validated_data):
        """
        :param instance: 要更新的数据对象  在视图中序列化器初始化是传递的数据对象
        :param validated_data: 验证后的字段数据
        :return:
        """
        # 直接进行字段更新
        # instance.btitle=validated_data['btitle']
        # instance.save()
        # 使用模型类方法进行更新
        res = BookInfo.objects.filter(id=instance.id).update(**validated_data)
        # 需要重新查询获取新的更新对象数据
        book = BookInfo.objects.get(id=instance.id)
        return book


class BookModelSerialzier(serializers.ModelSerializer):
    """
        ModelSerializer是模型类序列化器，和Serializer区别
        1、可以根据指定的模型类自动生成序列化器字段
        2、帮助实现了create业务逻辑和update业务逻辑
        3、帮助实现唯一值判断方法，只要在模型类中对字段指定了字段的唯一值选项参数（unique=True）则会自动进行判断
        除了这三点，其他使用形式都一样
    """
    # 显示指明字段
    # 修改字段选项第二种 指定定义字段
    bcomment = serializers.IntegerField(max_value=300, min_value=0, default=0)
    # 定义模型类种没有的字段 可以额外定义新字段
    # sms_code = serializers.CharField(max_length=6, min_length=6)

    class Meta:
        # 指定根据哪个模型类生成对应的序列化器字段
        model = BookInfo
        # 指定字段内容 __all__ 表示将所有模型类字段生成序列化器字段
        fields = "__all__"
        # 指定单独字段 元组中只有一个值是要逗号 元祖和列表两种形式都可以
        # fields = ('btitle',)
        # fields = ['btitle']
        # 指定字段取反
        # exclude = ('btitle', 'bread')

        # 修改字段的选项参数
        extra_kwargs = {
            # key值为要修改的字段,value值为修改的选项内容
            'bread': {
                'max_value': 100,
                'min_value': 0,
                'default': 0
            }
        }
